ソフトウェアアーキテクチャの基礎輪読会 9章
日付:2023/11/14
章:(アーキテクチャスタイル)基礎
調査者:naoya.icon
章のまとめ
アーキテクチャスタイル
以下は本書の定義
システム全体の構成を示している
以下の項目を包括的な構造と定義している
フロントエンドやバックエンドのソースコードがどのように編成されているか
そのソースコードがどのようにデータストアと相互作用するか
具体例
モノリス内のレイヤー
マイクロサービスとしての個別デプロイ
以下の意味合いも含んでいる
トポロジー
ノード(コンピューター、スイッチ、ルーター、など)がどのように相互に接続されているか
具体例:ネットワークトポロジー→接続形態
アーキテクチャ特性
メリデメ
特定の技術的課題に対する具体的な解決策を示している
設計構造を定義している
アーキテクチャスタイルの中で、特定の解決策を形成するのに役立つ低レベルの設計構造
具体例
特定の操作を実行する方法
サービス間で高いスケーラビリティ
パフォーマンスを実現する方法
※しかし、アーキテクチャスタイルはアーキテクチャパターンと呼ばれることもある
基礎的なパターン 9.1
ソフトウェアアーキテクチャの歴史にはいくつかの基礎的なパターンが登場する
理由:コードやデプロイ、アーキテクチャのその他の側面を整理する上で有用な視点を提供する
巨大な団子
アーキテクチャ構造が不在の状態を意味するアンチパターン
構造の欠如
直接的なデータベースアクセス
行き当たりばったりで構造化されたシステム
naoya.icon ハッカソンでありがち
takasshii.icon:wakaru
iNoma.iconこわいはなし
原因
全体的な構造が十分に定義されていなかった場合
最初は定義されていたが、認識できないほどに侵食されてしまった場合
具体例
内部構造を持たずに、イベントハンドラが直接データベースの呼び出しまで行っているようなコード
code:androdango.kt
class MainActivity : AppCompatActivity() {
// データベース接続用の変数
private lateinit var database: SQLiteDatabase
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// データベースの初期化
database = openOrCreateDatabase("appDatabase", MODE_PRIVATE, null)
database.execSQL("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)")
// ボタンにイベントリスナーを設定
val addButton = findViewById<Button>(R.id.addButton)
addButton.setOnClickListener {
addUser()
}
}
// ユーザーをデータベースに追加するメソッド
private fun addUser() {
val nameInput = findViewById<EditText>(R.id.nameInput)
val ageInput = findViewById<EditText>(R.id.ageInput)
val name = nameInput.text.toString()
val age = ageInput.text.toString().toInt()
// ContentValuesを使用してデータベースにユーザーを追加
val values = ContentValues().apply {
put("name", name)
put("age", age)
}
database.insert("users", null, values)
}
}
ユニタリーアーキテクチャ
単一のシステム
具体例
組み込みシステム
naoya.icon たかっしーが詳しそう?
takasshii.iconwakaranu
現在は、ほとんど存在しない
ソフトウェアが肥大化するにあたって、関心の分離が行われ、システムが分離 クライアント/サーバー
技術的な機能をフロントエンドとバックエンドで分離するスタイル
デスクトップ+データベースサーバー
(デスクトップ)アプリケーション(クライアント側)
ユーザーインターフェイス(UI)やプレゼンテーションロジックを担当
データベースサーバー(サーバー側)
データの格納、管理、複雑な計算やビジネスロジックを担当
このサーバーは通常、別の物理的なマシン上に設置され、ネットワークを通じてクライアントアプリケーションと通信する
ブラウザ+Webサーバー
ブラウザ(クライアント側)
ユーザーはブラウザ(上で動作するUI)を介してWebアプリケーションにアクセス
ブラウザは「薄いクライアント」として機能
主に表示と基本的なインタラクションを担当
Webサーバー(サーバー側)
Webサーバーはクライアントからのリクエストを受け取り、必要に応じてデータベースサーバーと通信してデータを取得・加工し、それをクライアントに返す
ビジネスロジックの処理、データ処理、APIの提供などを担当
3層
データベース層
データの永続化と管理を担当する層
データベースサーバーが該当
具体例
Oracle
SQL Server
アプリケーション層(ビジネスロジック層)
ビジネスルールやデータ処理ロジックを担う層
アプリケーションサーバーで実現され、クライアント層とデータベース層の間で仲介を担当
具体例
Java EEサーバー、.NETフレームワーク
プレゼンテーション層(フロントエンド)
ユーザーインターフェイスを提供する層
特徴
分散アーキテクチャの概念に基づいている
異なるシステム間でのデータや機能の共有を可能にする
現代のソフトウェア開発では、多くの場合、これらの複雑なプロトコルを直接扱う必要はない
メッセージキューやイベント駆動アーキテクチャのようなパターンやツールが、これらのプロトコルが提供していた機能を引き継いでいる
モノリシックアーキテクチャと分散アーキテクチャ
takasshii.icon:wakuwaku
モノリシック
分散型
分散アーキテクチャは以下の観点ではモノシリックアーキテクチャよりも有用である
パフォーマンス
スケーラビリティ
可用性
分散アーキテクチャの誤信
誤信1:ネットワークは信頼できる
ネットワークの信頼性は完全ではなく、時折接続の問題や遅延が発生する
takasshii.iconBLEより信頼性あるもん takasshii.icon分散アーキテクチャって通信はネットワークだけじゃないもん
分散アーキテクチャはネットワークに依存している
サービスやコンポーネントがネットワークを介して通信する
ネットワークに問題が発生すると、システム全体に影響を及ぼす危険性がある
分散アーキテクチャでシステムがネットワークに依存すればするほど、信頼性が低下する
タイムアウトは、一定時間内に応答がない場合に処理を中断する
サーキットブレーカーは、連続する失敗を検出した場合に一時的にサービス間の通信を遮断し、システムの安定性を保つ
誤信2:レイテンシーがゼロ
(ネットワーク)レイテンシー→インターネットや他のネットワークを通じてデータが移動するのにかかる時間 パーセンタイル→統計学においてデータセット内の値が特定の百分率以下に位置する点を示す尺度
50パーセンタイル(中央値): データセットのちょうど中央に位置する値で、50%のデータがこの値以下で、残りの50%がこの値以上
25パーセンタイル(第一四分位数): データセットの下位25%のデータがこの値以下である点
75パーセンタイル(第三四分位数): データセットの下位75%のデータがこの値以下である点
問題
多くのアーキテクトはレイテンシーを過小評価ないし、無視している
レイテンシーの影響は予想以上に大きい
実例
1リクエストあたりの平均レイテンシーが100ミリ秒だとすれば、10回の連続サービス呼び出しで合計1,000ミリ秒(1秒)の遅延が発生
takasshii.iconう
重要
平均レイテンシーとパーセンタイルを知ること
特に95%〜99%までのタイルを知ること
理由
平均レイテンシーが60m秒だったとしても、95パーセンタイルでは400ミリ秒の可能性がある
95パーセンタイルは、測定されたレイテンシー値の中で最も低い値から高い値までを並べたとき、全データの95%がこの値以下で、残りの5%がこの値を超える点を指す
全レイテンシー測定値の中で最も遅い5%が400ミリ秒以上である
誤信3:帯域幅は無限
帯域→データ通信において一定時間内に転送できる最大データ量
スタンプ結合→異なるモジュールやサービス間で不必要に多くのデータが共有される状況 コントラクト→クライアントとサービスの双方が合意した動作やデータ
※モノシリックアーキテクチャでは通常帯域幅は気にならない
理由:処理が一旦モノリスに入ると、そのビジネス要求を処理するのに必要な帯域幅はほとんどない
問題
分散アーキテクチャシステムでシステムを小さなデプロイメントユニット(サービス)に分割すると、サービス間の通信帯域を大きく消費する
サービス間の通信による帯域の消費は誤信1(ネットワークの信頼性)や誤信2(レイテンシー)に影響を与える
スタンプ結合が生じる
解決方法
プライベートなRESTful APIのエンドポイントを作成することで、必要なデータのみを提供する
takasshii.iconこれはサーバー側に頑張って欲しいAPIを作ってもらう感じかな
コントラクトでフィールドセレクターを用いることで、消費者が必要なデータフィールドのみを要求できるようにする
コントラクト→APIやサービス間のインターフェース契約
フィールドセレクターを利用することで、クライアントは必要なデータフィールドのみを指定してリクエストできる
takasshii.iconもう書かれてるんだ
GraphQL→クライアントがサーバーから必要なデータを厳密に指定して取得するクエリ言語
iNoma.iconエンドポイントが単一でよくなるんでしたっけ
クライアントは必要なデータ構造を自由に定義して、不要なデータの取得を避ける
kiri.iconが詳しいかったはず
CDC→サービス提供者と消費者(クライアント)間の契約を定義するアプローチ
naoya.icon よくわからんかった😭
内部メッセージングエンドポイントを用いることで、データの送信効率を向上させる
内部メッセージング→サービス間での非同期通信を実現するための方法
サービス間でメッセージ(データ)を交換する際に専用のメッセージングエンドポイントを使用する
naoya.icon Restful APIとの違いがわからん
takasshii.iconまとめて通信を行うモジュールを置いとく話かな?
重要
サービスやシステム間で最低限のデータ量を確実渡すことがこの誤診の対処法
誤信4:ネットワークは安全
問題
多くのアーキテクトや開発者はVPNや信頼できるネットワーク、ファイアウォールを使うことに慣れていて、ネットワークが安全ではないことを忘れがち
対処
分散型デプロイメントユニットの各エンドポイントは未知のリクエストや悪質なリクエストが到達しないようにセキュアな状態にする必要がある モノリスから分散型に移行すると、脅威や攻撃の表面積が飛躍的に増加
これがパフォーマンスが遅くなる原因にもなる
誤信5:トポロジーは決して変化しない
ネットワークで使用されるルーター、ハブ、スイッチ、ファイアウォールなど全て含まれる
トポロジーは変化する
具体例
ネットワークアップグレード
誤信6:管理者は1人だけ
問題
アークテクトは1人の管理者とコミュニケーションを取れば良いと思い込んでいる場合がある
現実
ネットワーク管理者が複数いる
大企業だと、特にそう(数十人)
対処
レイテンシー、トポロジーの変化についてネットワーク管理者とコミュニケーションを取る必要がある
vs モノシリック
単一のデプロイメントユニットなので、このやりとりを必要としない
誤信7:転送コストはゼロ
転送コスト→単純なAPIの呼び出しに関わる費用面の実コスト
課題
多くのソフトウェアアーキテクトは転送コストとレイテンシーを混同している
iNoma.icon転送コスト⊃レイテンシーということですかね
単純なAPI呼び出しをしたり、モノリシックなアプリケーションを分解するのに十分なインフラが整っているものと仮定するが、実際は異なる
naoya.icon これほんまか?笑
実際
ネットワークアクセスには転送コストがかかる
モノリスより分散型の方がかなり高くなる
対処
以下の観点で、現在のサーバーとネットワークのトポロジーを分析する
容量
帯域幅
レイテンシー
セキュリティゾーン
誤信8:ネットワークは均一
ほとんどの企業は複数のネットワークハードウェアベンダーを使ってインフラを構成している
全てのハードウェアベンダーがシームレスに統合できているわけではない
→ネットワークの信頼性、レイテンシーの仮定、帯域幅の仮定に影響を与える
takasshii.iconGoのアーキテクチャでAWSとGCPが1つのアプリとして動いて見たけどそのへん大変そう
この誤診は他のすべての誤診と結びついている
その他の分散アーキテクチャの課題
※本書の範囲外なので、簡単にまとめてある
分散ロギング
分散アーキテクチャでは、アプリケーションやシステムのログが分散されている
問題
特定の命令が却下された原因を特定するために根本原因を分析するのは難しく、時間がかかる
異なるログが異なる場所に配置され、異なるフォーマットで含まれている
ログの統合ツールは1つの統合されたログやコンソールへまとめるのに役立つが、分散ロギングに関わる複雑さの表面を掻き分けているわけではない
naoya.icon それでいいのでは?ただ、原因が分かっても、解決するのは大変そう
分散トランザクション
トランザクション→データベースやその他のデータストレージシステムにおける一連の操作の単位
課題
分散アーキテクチャでは、別々のデプロイメントユニットによって処理されたデータが、任意の時点ですべて同期されて一貫した状態になることを、結果整合性によって保証される
vsモノリス
永続化フレームワークから実行される標準的なコミットやトランザクションはACIDトランザクションを利用して、データが正しい方法で更新され、高いデータの一貫性を保証する 原子性 (Atomicity)
すべての操作は、全部または全く実行されない
「すべてか無か」の原則
一貫性 (Consistency)
データベースを一貫した状態から別の一貫した状態へと移行
隔離性 (Isolation)
同時に実行される複数のトランザクションは互いに干渉しない
持続性 (Durability)
トランザクションが完了したら、その結果は永続的にデータベースに記録される
コントラクトの作成、保守、バージョン管理、メンテナンス
管理方法
サーガ→補償用のイベントソーシングか有限状態の機会を利用する
BASEトランザクションを利用
Basically Available(基本的に利用可能)
Soft state(柔軟な状態)→データソース間で不整合を許容し、外部から届いた情報で状態が決定される
Eventual consistency(結果整合性)→"基本的に利用可能"なシステムやサービスに基づいて、アーキテクチャパターンやメッセージングを使用することで担保する
分散型アーキテクチャはデータの整合性や完全性を犠牲にすることで、高いスケーラビリティ、パフォーマンス、可用性を実現(トレードオフ)している
応用
code:sample.kt
質疑応答